/*
 * Copyright 2011, 2012 Fabian Wenzelmann
 * 
 * This file is part of Recognition-Procedures-for-Boolean-Functions.
 * 
 * Recognition-Procedures-for-Boolean-Functions is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Recognition-Procedures-for-Boolean-Functions is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Recognition-Procedures-for-Boolean-Functions.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.wenzelmf.booleanFunction.lpb.combinatorial.splittingTree;

import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;

import de.wenzelmf.booleanFunction.lpb.combinatorial.occurrencePattern.OccurrencePattern;

/**
 * A variable setting is used to save global information of the variables (position in the sorted
 * occurrence pattern sequence etc.).
 * 
 * @author Fabian Wenzelmann
 * @version 1.0
 *
 */
public class VariableSetting implements Iterable<String>, Cloneable
{
	/**
	 * Stores the namens of the variables (sorted according the occurrence patterns of the
	 * initial DNF).
	 */
	protected String[] sortedVariables;
	
	/**
	 * Stores for each variable name the position in the initial sorted occurrence pattern sequence.
	 */
	protected HashMap<String, Integer> positionMap;
	
	/**
	 * Construct a new setting given the variables (sorted according the order on occurrence
	 * patterns).
	 *  
	 * @param sortedVariables The variables of the DNF sorted according the occurrence patterns.
	 */
	public VariableSetting(String[] sortedVariables)
	{
		this.sortedVariables =  new String[sortedVariables.length];
		// TODO: size OK?
		this.positionMap = new HashMap<String, Integer>( sortedVariables.length * 2 );
		int pos = 0;
		for(String variable : sortedVariables)
		{
			this.sortedVariables[pos] = variable;
			this.positionMap.put(variable, pos++);
		}
	}
	
	/**
	 * Construct a new setting given the sorted occurrence patterns.
	 *  
	 * @param sortedPatterns The sorted occurrence patterns of all variables in the DNF.
	 */
	public VariableSetting(OccurrencePattern[] sortedPatterns)
	{
		this.sortedVariables = new String[sortedPatterns.length];
		this.positionMap = new HashMap<String, Integer>(sortedPatterns.length * 2);
		int pos = 0;
		for(OccurrencePattern p : sortedPatterns)
		{
			String variable = p.getVariable();
			this.sortedVariables[pos] = variable;
			this.positionMap.put(variable, pos++);
		}
	}
	
	// TODO
	// TODO: use this constructor everywhere we use the old constructors?
	protected VariableSetting(String[] sortedVariables, HashMap<String, Integer> positionMap)
	{
		this.sortedVariables = sortedVariables;
		this.positionMap = positionMap;
	}
	
	/**
	 * Return the position of the variable with name {@code variableName} in the occurrence pattern
	 * sequence of the initial DNF.
	 * 
	 * It returns {@code null} if there was no variable with the specified name in the DNF.
	 * 
	 * @param variableName The name of the variable to look up.
	 * 
	 * @return The position of the variable with name {@code variableName} in the occurrence pattern
	 * sequence of the initial DNF, {@code null} if there was no variable with {@code variableName}
	 * in the initial DNF.
	 */
	public Integer getPos(String variableName)
	{
		return positionMap.get(variableName);
	}
	
	/**
	 * Return the variable on position {@code pos} in the occurrence pattern sequence of the
	 * initial DNF (first index is 0).
	 * 
	 * It throws an {@code IndexOutOfBoundsException} if {@code pos} is not a valid index of the
	 * sequence.
	 * 
	 * @param pos The position to look up.
	 * 
	 * @return The variable in the initial occurrence pattern sequence on position {@code pos}.
	 * 
	 * @throws IndexOutOfBoundsException If {@code pos} is not a valid index of the sequence.
	 */
	public String getVariable(int pos)
	{
		return sortedVariables[pos];
	}
	
	/**
	 * Returns an iterator over all variables in the variable sequence (sorted according the
	 * occurrence patterns).
	 * 
	 * @return An iterator over all variables in the variable sequence (sorted according the
	 * occurrence patterns).
	 */
	public Iterator<String> iterator()
	{
		// TODO: test this method?
		return new Iterator<String>()
		{
			
			int pos = 0;
			
			@Override
			public boolean hasNext()
			{
				return pos < sortedVariables.length;
			}

			@Override
			public String next()
			{
				try
				{
					return sortedVariables[pos++];
				}
				catch(IndexOutOfBoundsException e)
				{
					throw new NoSuchElementException(e.toString());
				}
			}

			@Override
			public void remove()
			{
				throw new UnsupportedOperationException("Removing elements from a VariableSetting is not allowed.");
			}
		};
	}
	
	/**
	 * Return a String representation of this setting, that is [x<sub>1</sub>, &hellip;, x<sub>m</sub>]
	 * where  x<sub>1</sub>, &hellip;, x<sub>m</sub> is the ordered sequence of the variables.
	 * 
	 * @return String representation of this VariableSetting, returns the string [x<sub>1</sub>, &hellip;, x<sub>m</sub>]
	 * where  x<sub>1</sub>, &hellip;, x<sub>m</sub> is the ordered sequence of the variables.
	 */
	@Override
	public String toString()
	{
		StringBuffer result = new StringBuffer("[");
		boolean first = true;
		for(String variable : this)
		{
			if(first)
			{
				first = false;
				result.append(variable);
			}
			else
			{
				result.append(", ");
				result.append(variable);
			}
		}
		result.append("]");
		return result.toString();
	}
	
	/**
	 * Return the number of variables in this setting.
	 * 
	 * @return Number of variables in this setting.
	 */
	public int size()
	{
		return sortedVariables.length;
	}
	
}
